#include "crc16.h"

void create_crc16_table16(CRC16_16 * p_crc16_16) {
    unsigned short index;
    unsigned short i;
    unsigned short poly;
    unsigned short tmp;

    if(p_crc16_16->crcRefIn) {
        poly = 0x0000;
        for(i = 0; i < 16; i++) {
            poly = ((0x0001 << i) & p_crc16_16->crcPoly) == 0 ? poly : (poly | (0x8000 >> i));
        }
        for(index = 0; index < 16; index++) {
            tmp = index;
            for(i = 4; i > 0; i--) {
                tmp = (tmp & 0x0001) == 0x0001 ? ((tmp >> 1) ^ poly) : (tmp >> 1);
            }
            p_crc16_16->crcTable[index] = tmp;
        }
    }
    else {
        poly = p_crc16_16->crcPoly;
        for(index = 0; index < 16; index++) {
            tmp = index << 12;
            for(i = 4; i > 0; i--) {
                tmp = (tmp & 0x8000) == 0x8000 ? ((tmp << 1) ^ poly) : (tmp << 1);
            }
            p_crc16_16->crcTable[index] = tmp;
        }
    }
}

void create_crc16_table256(CRC16_256 * p_crc16_256) {
    unsigned short index;
    unsigned short i;
    unsigned short tmp;
    unsigned short poly;

    if(p_crc16_256->crcRefIn) {
        poly = 0x0000;
        for(i = 0; i < 16; i++) {
            poly = ((0x0001 << i) & p_crc16_256->crcPoly) == 0 ? poly : (poly | (0x8000 >> i));
        }
        for(index = 0; index < 256; index++) {
            tmp = index;
            for(i = 8; i > 0; i--) {
                tmp = (tmp & 0x0001) == 0x0001 ? ((tmp >> 1) ^ poly) : (tmp >> 1);
            }
            p_crc16_256->crcTable[index] = tmp;
        }
    }
    else {
        poly = p_crc16_256->crcPoly;
        for(index = 0; index < 256; index++) {
            tmp = index << 8;
            for(i = 8; i > 0; i--) {
                tmp = (tmp & 0x8000) == 0x8000 ? ((tmp << 1) ^ poly) : (tmp << 1);
            }
            p_crc16_256->crcTable[index] = tmp;
        }
    }
}

void prepare_crc16_with_table16(CRC16_16 * p_crc, unsigned short * crc16)
{
    unsigned short i;
    unsigned short tmp;
    if(p_crc->crcRefIn) {
        tmp = 0x0000;
        for (i = 0; i < 16; i++) {
            tmp = ((0x0001 << i) & p_crc->crcInit) == 0 ? tmp : (tmp | (0x8000 >> i));
        }
    }
    else {
        tmp = p_crc->crcInit;
    }
    *crc16 = tmp;
}

void process_crc16_with_table16(CRC16_16 * p_crc, const unsigned char * character, unsigned short * crc16)
{
    unsigned short tmp;
    tmp = *crc16;
    if(p_crc->crcRefIn) {
        tmp = p_crc->crcTable[(*character ^ (unsigned char)tmp) & 0x0f] ^ (tmp >> 4);
        tmp = p_crc->crcTable[((*character >> 4) ^ (unsigned char)tmp) & 0x0f] ^ (tmp >> 4);
    }
    else {
        tmp = p_crc->crcTable[(*character >> 4) ^ (tmp >> 12)] ^ (tmp << 4);
        tmp = p_crc->crcTable[(*character & 0x0f) ^ (tmp >> 12)] ^ (tmp << 4);
    }
    *crc16 = tmp;
}

void complete_crc16_with_table16(CRC16_16 * p_crc, unsigned short * crc16)
{
    unsigned short i;
    unsigned short tmp;
    if((p_crc->crcRefIn !=0 && p_crc->crcRefOut != 0) || (p_crc->crcRefIn == 0 && p_crc->crcRefOut == 0)) {
        tmp = *crc16 ^ p_crc->crcXor;
    }
    else {
        tmp = 0x0000;
        for(i = 0; i < 16; i++) {
            tmp = ((0x0001 << i) & *crc16) == 0 ? tmp : (tmp | (0x8000 >> i));
        }
        tmp = tmp ^ p_crc->crcXor;
    }
    *crc16 = tmp;
}

void prepare_crc16_with_table256(CRC16_256 * p_crc, unsigned short * crc16)
{
    unsigned short i;
    unsigned short tmp;
    if(p_crc->crcRefIn) {
        tmp = 0x0000;
        for (i = 0; i < 16; i++) {
            tmp = ((0x0001 << i) & p_crc->crcInit) == 0 ? tmp : (tmp | (0x8000 >> i));
        }
    }
    else {
        tmp = p_crc->crcInit;
    }
    *crc16 = tmp;
}

void process_crc16_with_table256(CRC16_256 * p_crc, const unsigned char * character, unsigned short * crc16)
{
    unsigned short tmp;
    tmp = *crc16;
    if(p_crc->crcRefIn) {
        tmp = p_crc->crcTable[*character ^ (unsigned char)tmp] ^ (tmp >> 8);
    }
    else {
        tmp = p_crc->crcTable[*character ^ tmp >> 8] ^ (tmp << 8);
    }
    *crc16 = tmp;
}

void complete_crc16_with_table256(CRC16_256 * p_crc, unsigned short * crc16)
{
    unsigned short i;
    unsigned short tmp;
    if((p_crc->crcRefIn !=0 && p_crc->crcRefOut != 0) || (p_crc->crcRefIn == 0 && p_crc->crcRefOut == 0)) {
        tmp = *crc16 ^ p_crc->crcXor;
    }
    else {
        tmp = 0x0000;
        for(i = 0; i < 16; i++) {
            tmp = ((0x0001 << i) & *crc16) == 0 ? tmp : (tmp | (0x8000 >> i));
        }
        tmp = tmp ^ p_crc->crcXor;
    }
    *crc16 = tmp;
}

unsigned short generate_crc16(void * p_crc, unsigned int crc_table_size, const unsigned char * data, unsigned int length)
{
    unsigned int i;
    unsigned short tmp;
    unsigned char character;
    unsigned short crc_init;
    unsigned short crc_xor;
    unsigned char crc_refin;
    unsigned char crc_refout;
    unsigned short * crc_table;

    if(crc_table_size == 16) {
        crc_init = ((CRC16_16 *)p_crc)->crcInit;
        crc_xor = ((CRC16_16 *)p_crc)->crcXor;
        crc_refin = ((CRC16_16 *)p_crc)->crcRefIn;
        crc_refout = ((CRC16_16 *)p_crc)->crcRefOut;
        crc_table = ((CRC16_16 *)p_crc)->crcTable;
    }
    else if(crc_table_size == 256) {
        crc_init = ((CRC16_256 *)p_crc)->crcInit;
        crc_xor = ((CRC16_256 *)p_crc)->crcXor;
        crc_refin = ((CRC16_256 *)p_crc)->crcRefIn;
        crc_refout = ((CRC16_256 *)p_crc)->crcRefOut;
        crc_table = ((CRC16_256 *)p_crc)->crcTable;
    }
    else {
        return 0;
    }

    if(crc_refin) {
        tmp = 0x0000;
        for (i = 0; i < 16; i++) {
            tmp = ((0x0001 << i) & crc_init) == 0 ? tmp : (tmp | (0x8000 >> i));
        }
    }
    else {
        tmp = crc_init;
    }

    if(crc_table_size == 16) {
        for(i = 0; i < length; i++) {
            character = data[i];
            if(crc_refin) {
                tmp = crc_table[(character ^ (unsigned char)tmp) & 0x0f] ^ (tmp >> 4);
                tmp = crc_table[((character >> 4) ^ (unsigned char)tmp) & 0x0f] ^ (tmp >> 4);
            }
            else {
                tmp = crc_table[(character >> 4) ^ (tmp >> 12)] ^ (tmp << 4);
                tmp = crc_table[(character & 0x0f) ^ (tmp >> 12)] ^ (tmp << 4);
            }
        }
    }
    else {
        for(i = 0; i < length; i++) {
            character = data[i];
            if(crc_refin) {
                tmp = crc_table[character ^ (unsigned char)tmp] ^ (tmp >> 8);
            }
            else {
                tmp = crc_table[character ^ tmp >> 8] ^ (tmp << 8);
            }
        }
    }

    if((crc_refin !=0 && crc_refout != 0) || (crc_refin == 0 && crc_refout == 0)) {
        tmp = tmp ^ crc_xor;
    }
    else {
        crc_init = tmp;
        tmp = 0x0000;
        for(i = 0; i < 16; i++) {
            tmp = ((0x0001 << i) & crc_init) == 0 ? tmp : (tmp | (0x8000 >> i));
        }
        tmp = tmp ^ crc_xor;
    }

    return tmp;
}
